EAPI=4
inherit eutils mount-boot user

DESCRIPTION="Linux kernel"
HOMEPAGE="http://www.kernel.org/"

[[ ${PV} =~ ^([0-9]+"."[0-9]+)((_|".").+)?$ ]]
LINUX_DIRVER=${BASH_REMATCH[1]}
if [[ $LINUX_DIRVER != "2.6" ]]; then
	LINUX_DIRVER=3.0
fi
if [[ ${PV} =~ ^(.*)_rc([0-9]+)$ ]]; then
	LINUX_NAME="${PN}-${BASH_REMATCH[1]}-rc${BASH_REMATCH[2]}"
	LINUX_PATH="v${LINUX_DIRVER}/testing/${LINUX_NAME}.tar.xz"
else
	LINUX_NAME="${P}"
	LINUX_PATH="v${LINUX_DIRVER}/${LINUX_NAME}.tar.xz"
fi
SRC_URI="http://www.kernel.org/pub/linux/kernel/${LINUX_PATH}"

LICENSE="GPL-2"
SLOT="${PV}"
KEYWORDS="amd64 x86"
IUSE="+initrd"

DEPEND="initrd? ( sys-kernel/ainitramfs )"
RDEPEND=""

S="${WORKDIR}/${LINUX_NAME}"

parse_ebuild_kernel_version() {
	local version=$1

	if [[ $version =~ ^("3."[0-9]+|"2.6."[0-9]+)("."[0-9]+)?(_rc[0-9]+)?$ ]]; then
		echo "${BASH_REMATCH[1]}"
	else
		die "malformed kernel version"
	fi
}

find_kernel_config() {
	kernel_config="${ROOT}"/etc/portage/savedconfig/linux-config-${PV}-${kernel_arch}
	[[ -e ${kernel_config} ]] && return 0

	kernel_config="${ROOT}"/etc/portage/savedconfig/linux-config-${base_kernel_version}-all-${kernel_arch}
	[[ -e ${kernel_config} ]] && return 0

	kernel_config="${FILESDIR}"/linux-config-${PV}-${kernel_arch}
	[[ -e ${kernel_config} ]] && return 0

	kernel_config="${FILESDIR}"/linux-config-${base_kernel_version}-all-${kernel_arch}
	[[ -e ${kernel_config} ]] && return 0

	die "cannot find kernel config"
}

src_prepare() {
	# allow user to easily apply patches
	epatch_user
}

src_configure() {
	# Choose kernel arch.
	case "${ARCH}" in
		amd64) kernel_arch=x86_64;;
		x86) kernel_arch=x86;;
		*) die "unknown ARCH";;
	esac

	# Determine base kernel version.
	base_kernel_version=$(parse_ebuild_kernel_version ${PV})

	# Choose config.
	find_kernel_config
	einfo "Using kernel config file: ${kernel_config}"

	# Allow overriding compiler.
	sed -i 's/$(CROSS_COMPILE)gcc/$(if $(KERNEL_CC), $(KERNEL_CC), $(CROSS_COMPILE)gcc)/g' Makefile || die
}

src_compile() {
	# Build kernel in separate build dir.
	einfo "Building kernel"
	mkdir "${WORKDIR}"/build || die
	cp "${kernel_config}" "${WORKDIR}"/build/.config || die
	emake ARCH=${kernel_arch} O="${WORKDIR}"/build || die

	# Prepare build dir for installation.
	einfo "Preparing build dir for installation"
	mkdir "${WORKDIR}"/build-install || die
	cp "${kernel_config}" "${WORKDIR}"/build-install/.config || die
	emake ARCH=${kernel_arch} O="${WORKDIR}"/build-install oldconfig || die
	emake ARCH=${kernel_arch} O="${WORKDIR}"/build-install modules_prepare || die
	cp "${WORKDIR}"/build/{System.map,Module.symvers} "${WORKDIR}"/build-install/ || die
}

src_install() {
	# Setup lib dir.
	mkdir "${D}/$(get_libdir)" || die
	if [ "$(get_libdir)" != lib ]; then
		ln -s "$(get_libdir)" "${D}"/lib || die
	fi

	# Install modules.
	einfo "Installing modules"
	emake ARCH=${kernel_arch} O="${WORKDIR}"/build INSTALL_MOD_PATH="${D}" _modinst_ || die

	# Determine kernel version.
	kernel_version=$( cd "${D}"/lib/modules; echo * )

	# Install firmware.
	einfo "Installing firmware"
	mkdir -p "${D}"/lib/firmware || die # https://bugzilla.kernel.org/show_bug.cgi?id=39192
	emake ARCH=${kernel_arch} O="${WORKDIR}"/build INSTALL_MOD_PATH="${D}" firmware_install || die

	# Move firmware to kernel version specific directory.
	mv "${D}/lib/firmware" "${D}/lib/firmware-x" || die
	mkdir "${D}/lib/firmware" || die
	mv "${D}/lib/firmware-x" "${D}/lib/firmware/${kernel_version}" || die

	# Install kernel.
	mkdir "${D}"/boot || die
	emake ARCH=${kernel_arch} O="${WORKDIR}"/build INSTALL_PATH="${D}"/boot install || die

	# Install source.
	einfo "Installing kernel source"
	mkdir -p "${D}"/usr/src/ || die
	cp -r "${S}" "${D}"/usr/src/linux-${kernel_version} || die

	# Install build dir.
	einfo "Installing build dir for external modules"
	cp -r "${WORKDIR}"/build-install "${D}"/usr/src/build-${kernel_version} || die

	# Fix links in build dir.
	rm "${D}"/usr/src/build-${kernel_version}/source || die
	ln -s ../linux-${kernel_version} "${D}"/usr/src/build-${kernel_version}/source || die

	# Fix source path in build dir Makefile.
	sed -i 's|^MAKEARGS := .*$|MAKEARGS := -C /usr/src/linux-'${kernel_version}'|' "${D}"/usr/src/build-${kernel_version}/Makefile || die

	# Temporary fix for external modules that don't about version.h moving.
	cp "${D}"/usr/src/build-${kernel_version}/include/{generated/uapi/,}linux/version.h || die

	# Fix links in modules dir.
	rm "${D}"/lib/modules/${kernel_version}/source || die
	rm "${D}"/lib/modules/${kernel_version}/build || die
	ln -s ../../../usr/src/linux-${kernel_version} "${D}"/lib/modules/${kernel_version}/source || die
	ln -s ../../../usr/src/build-${kernel_version} "${D}"/lib/modules/${kernel_version}/build || die

	einfo "Generating initramfs"

	use_yesno() {
		if use "$1"; then echo yes; else echo no; fi
	}

	# Workaround faulty sandbox violations when ldconfig chroots.
	# (we do not actually write these files on the host)
	addwrite /etc/ld.so.cache
	addwrite /etc/ld.so.cache~

	if use initrd; then
		# Read additional ainitramfs arguments from config file.
		local ainitramfs_args=()
		local ainitramfs_config="${ROOT}"/etc/portage/savedconfig/ainitramfs.sh
		if [[ -e ${ainitramfs_config} ]]; then
			. "${ainitramfs_config}" || die "failed to source ${ainitramfs_config}"
		fi

		# Generate initramfs.
		ainitramfs \
			--root "${ROOT}" \
			--kmodroot "${D}" \
			--kernel ${kernel_version} \
			--toolchain "${CHOST}-" \
			--imageout "${D}"/boot/initrd-${kernel_version} \
			"${ainitramfs_args[@]}"
	fi

	# Remove lib symlink if created.
	if [ "$(get_libdir)" != lib ]; then
		rm "${D}"/lib || die
	fi
}

pkg_postinst() {
	# Update module dependencies.
	einfo "Updating module dependencies"
	depmod -b "${ROOT}" -F "${ROOT}"/usr/src/build-${kernel_version}/System.map ${kernel_version} || die
}
